package ch.trackedbean.loaders.internal;

import java.util.*;
import java.util.concurrent.*;
import ch.trackedbean.internal.*;
import ch.trackedbean.internal.ClassStructureHelper.IVisitor;
import ch.trackedbean.loaders.*;

/**
 * Helper class for managing {@link ILoaderService}s.<br>
 * This class is thread safe.<br>
 * The manager loads all available services using {@link ServiceLoader} during it's initialization.
 * 
 * @param <T> The service type
 * @author M. Hautle
 */
public class LoaderList<T extends ILoaderService> {
    /** Flag indicating if we are running under OSGi or not. */
    private static final boolean OSGI = System.getProperty("osgi.os") != null;

    /** All services which were registered on a concrete type. */
    private final Map<Class, T> concrete = new ConcurrentHashMap<Class, T>();

    /** All services which were registered on super types. */
    private final Map<Class, T> superType = new ConcurrentHashMap<Class, T>();

    /** The visitor to use for the {@link #superType} examination. */
    private final Visitor visitor = new Visitor();

    /**
     * Default constructor.
     * 
     * @param serviceType The service interface
     */
    public LoaderList(Class<T> serviceType) {
        for (Iterator<T> i = ServiceLoader.load(serviceType).iterator(); i.hasNext();)
            registerService(i.next());
        if (OSGI)
            LoaderListSericeTracker.register(this, serviceType);
    }

    /**
     * Returns the loader service for the given class.
     * 
     * @param c The class
     * @return The loader service
     * @throws NoLoaderException If no service was registered for the class
     */
    public T getService(Class c) throws NoLoaderException {
        T s = concrete.get(c);
        if (s != null)
            return s;
        s = ClassStructureHelper.visit(c, visitor);
        if (s == null)
            throw new NoLoaderException(c);
        return s;
    }

    /**
     * Registers the given service.
     * 
     * @param service The service to register
     */
    public void registerService(T service) {
        if (service.isSuperLoader())
            superType.put(service.getType(), service);
        else
            concrete.put(service.getType(), service);
    }

    /**
     * Removes the loader for the given type specification.
     * 
     * @param type The type ({@link ILoaderService#getType()})
     * @param superLoader True if the loader to remove is a super loader ({@link ILoaderService#isSuperLoader()})
     */
    public void unregisterService(Class type, boolean superLoader) {
        if (superLoader)
            superType.remove(type);
        else
            concrete.remove(type);
    }

    /**
     * Clears all service registrations.
     */
    public void clear() {
        superType.clear();
        concrete.clear();
    }

    /**
     * The visitor for examining the {@link LoaderList#superType} values.<br>
     * Implemented as inner class to avoid other public methods on {@link LoaderList}.
     * 
     * @author M. Hautle
     */
    private class Visitor implements IVisitor<T> {
        /**
         * {@inheritDoc}
         */
        @Override
        public T visitInterface(Class c) {
            return superType.get(c);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public T visitClass(Class c) {
            return superType.get(c);
        }
    }
}
